#pragma once
#include <map>
#include <sstream>

#include "util.h"
#include "invoker.h"
#include "serializer.h"


namespace TTP {

   template<typename C, typename... Args>
   long long hash(void (C::*fp)(Args...)) {
      long long ret;
      memcpy(&ret, (void*)&fp, sizeof(ret));
      return ret;
   }

   template<typename... Args>
   long long hash(void (*fp)(Args...)) {
      long ret;
      memcpy(&ret, (void*)&fp, sizeof(ret));
      return ret;
   }

   class dispatcher {
   protected:
      int cur_id;
      std::map<long long, int>   id_map;
      std::map<int, invokable*> ivk_map;
      std::vector<byte> buffer;

      pthread_mutex_t lock;

      inline void __applyVector(const std::vector<byte>& vec) {
         std::vector<byte>::const_iterator it = vec.begin();
         const std::vector<byte>::const_iterator end = vec.end();
         int fid; 
         
         while(it != vec.end()) {
            unserialize(it, end, fid);
            if(ivk_map.find(fid) == ivk_map.end()) {
               std::ostringstream os;
               os << "No such function: " << fid;
               throw msg_exception(os.str());
            } else {
               (*ivk_map[fid])(it, end);
            }
         }
      }

   public:

      dispatcher() : cur_id(0) {
         pthread_mutex_init(&lock, 0);
      }

      std::vector<byte>* getBuffer() {
         lockObj lk(&lock);
         std::vector<byte> *buf = new std::vector<byte>(buffer);
         buffer.clear();
         return buf;
      }

      template<typename... Args>
      inline void Register(void (*fp)(Args...)) {
         lockObj lk(&lock);
         if(id_map.find(hash(fp)) == id_map.end()) {
            id_map.insert(std::make_pair(hash(fp), cur_id));
            ivk_map.insert(std::make_pair(cur_id++, 
                     (invokable*)(new invoker<decltype(fp)>(fp))));
         } else {
            throw msg_exception("Can't register function twice!");
         }
      }

      template<typename C, typename... Args>
      inline void Register(C* obj, void (C::*fp)(Args...)) {
         lockObj lk(&lock);
         if(id_map.find(hash(fp)) == id_map.end()) {
            id_map.insert(std::make_pair(hash(fp), cur_id));
            ivk_map.insert(std::make_pair(cur_id++, 
                     (invokable*)(new invoker<decltype(fp)>(obj, fp))));
         } else {
            throw msg_exception("Can't register function twice!");
         }
      }

      template<typename... Args, typename... VArgs>
      inline void call_remote(void (&fp)(Args...), const VArgs&... vags) {
         lockObj lk(&lock);
         if(id_map.find(hash(fp)) == id_map.end()) {
            throw msg_exception("Calling unregistered function!");
         }

         serialize(buffer, id_map.find(hash(fp))->second );
         serializer<Args...>::run(buffer, vags...);
      }

      template<typename C, typename... Args, typename... VArgs>
      inline void call_remote(void (C::*fp)(Args...), const VArgs&... vags) {
         lockObj lk(&lock);
         if(id_map.find(hash(fp)) == id_map.end()) {
            throw msg_exception("Calling unregistered function!");
         }

         serialize(buffer, id_map.find(hash(fp))->second );
         serializer<Args...>::run(buffer, vags...);
      }


      inline void autoExecute() {
         lockObj lk(&lock);
         __applyVector(buffer);
         buffer.clear();
      }

      inline void applyVector(const std::vector<byte>& vec) {
         lockObj lk(&lock);
         __applyVector(vec);
      }

      virtual ~dispatcher() {
         FOR_EACH(pr, ivk_map) { delete pr->second; }
         pthread_mutex_destroy(&lock);
      }

   };
}
